package com.google.code.cisx.store.jpa;

import java.io.File;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Properties;

import org.junit.After;
import org.junit.Assert;

import com.google.code.cisx.model.CommonUpdatableEntity;
import com.google.code.cisx.store.StoreModule;
import com.google.code.cisx.store.api.Query;
import com.google.code.cisx.store.api.Store;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.TypeLiteral;

/**
 * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
 * @version $Id: EntityTestSupport.java 258 2008-11-29 06:23:51Z rahul.thakur.xdev $
 * @since 1.0
 */
public abstract class EntityTestSupport extends StoreTestSupport {

	@Inject
	private Injector injector;

	public EntityTestSupport() {
		super();
	}

	/**
	 * Expects a path to the SQL script be passed in. This is used to load the
	 * initial test data into the test Database.
	 * 
	 * @param sql
	 *          initial test data to seed the test database with.
	 */
	public void setUp(String... sql) {
		Properties propMap = new Properties();
		setUp(propMap);

		for (int i = 0; i < sql.length; i++) {
			File sqlScript = new File(sql[i]);
			Assert.assertTrue("Unable to find test data resource: " + sqlScript.getAbsolutePath(), sqlScript.exists());
			loadSqlSource(sqlScript);
		}
		StoreModule storeModule = new StoreModule();
		Guice.createInjector(storeModule).injectMembers(this);
		// inject Store impl into this test
		//injector.injectMembers(this);
		super.setUp();
	}

	@After
	public void teardown() throws Exception {
		File ddl = new File("src/test/resources/sql/clear-tables-ddl.sql");
		if (ddl.exists())
			loadSqlSource(ddl);
		this.tearDown();
	}

	@Override
	protected String getPersistenceUnitName() {
		return "cisx-store";
	}

	protected <T extends CommonUpdatableEntity> Store<T, Query<T>> getStoreInstance(final Class<T> entity) {

		final ParameterizedType parameterizedQueryType = new ParameterizedType() {

			public Type[] getActualTypeArguments() {
				return new Type[] { entity };
			}

			public Type getOwnerType() {
				return null;
			}

			public Type getRawType() {
				return Query.class;
			}
		};

		ParameterizedType parameterizedStoreType = new ParameterizedType() {

			public Type[] getActualTypeArguments() {
				// Here we tell the runtime that one of these Type arguments is
				// another ParameterizedType. Example: Store<Project, Query<Project>>
				return new Type[] { entity, parameterizedQueryType };
			}

			public Type getOwnerType() {
				return null;
			}

			public Type getRawType() {
				return Store.class;
			}
		};

		TypeLiteral<?> type = TypeLiteral.get(parameterizedStoreType);

		if (injector.findBindingsByType(type).size() > 0) {
			return (Store<T, Query<T>>) injector.findBindingsByType(type).get(0).getProvider().get();
		}

		return null;
	}

}